#include "LevelScene.h"
#include "PauseScene.h"
#include "MenuScene.h"
#include "GameOverScene.h"
#include "ChangeZonaScene.h"
#include "MusicManager.h"
#include <iomanip>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iostream>



//---*A esta funcion se le ingresa el mapeo en el que queremos buscar un dato
//---*el segundo parametro indica la posicion que buscamos
//---*por ultimo, el 3er parametro indica que respuesta queremos que devuelva, es decir, si desde el 1er o 2do dato del mapeo
template <class T,class F>
F LevelScene::BuscoZonaenMapa(std::map<T,F> Map,int IDZona){
std::map<T,F>::iterator p=Map.find(IDZona);
    if(p != Map.end()){
	F dato;
	dato=p->second;
	return dato;
	}
	else
	{	
	F dato="NULL";
	}
}



//---*Construye la Escena que se encargara de administrar como Nivel:
LevelScene::LevelScene(const std::string &NivelFileTXT){		
	this->NivelFileTXT=NivelFileTXT;
    this->changetoMenu=false;
	this->changeZona=false;
	this->m_mustUpdateCurrentZona=false;
    this->m_mustChangeMusic=false;
	this->changetoMenu=false;                    
	this->pause=false;  
	zonaActual=NULL;
	zonaAeliminar=NULL;
}
//---*Destruye la Escena:
LevelScene::~LevelScene(){}
//---*Limpia la Escena:
void LevelScene::Cleanup(){}

void LevelScene::Init(){
int IDZona=0;
std::string aux;
LoadTXT(NivelFileTXT);
//---*Buscamos el nombre del archivo TXT de la Zona que se quiere cargar:
aux=BuscoZonaenMapa(mapZonas,IDZona);
if(aux=="NULL"){ //---*Si la Zona ingresada NO existe, iniciamos en la 1era Zona que figure en el map:
aux=BuscoZonaenMapa(mapZonas,0);
cout<<"LA ZONA QUE SE QUIERE INICIAR NO EXISTE EN EL NIVEL, SE INICIARA EN LA ZONA 0"<<endl;
}

Zona *z=new Zona(aux,0);
m_zonasDeque.push_back(z);
zonaActual = z;
zonaActual->Init();
//---*Creamos otros objetos necesarios:
const sf::Image &tex = TextureManager::GetInstance().GetTexture("../data/Sprites/energia.png");	
energia.SetImage(tex);
energia.SetScale(0.3f,0.3f);
zonaActual->Aniade_Player1(theEngine.Get_Player1());
musicFile=zonaActual->music;

MusicManager::GetInstance().Play(musicFile);


}


bool LevelScene::LoadTXT(const std::string NivelFileTXT){
	//---*Archivo del TXT del Nivel donde nos indica todas sus Zonas.
	std::ifstream OpenFile(NivelFileTXT);
	//---*Creamos un string auxiliar para ir almacenando las lineas leidas:
	std::string Line;	
	//---*Leemos todas las lineas del TXT:
	if(OpenFile.is_open())
	{   
		//---*Mientras que no se llegue al final del TXT:
		while(!OpenFile.eof())
		{
		std::getline(OpenFile,Line); //---*Tomamos la 1er linea del TXT:
		//---*Si la 1er linea es "ZONAS":
		if(Line.find("ZONAS") != std::string::npos)
		{			
			while(!OpenFile.eof())
		    {
			std::getline(OpenFile,Line); //---*Tomamos la siguiente linea de "ZONAS":
			if(Line.find("<") != std::string::npos)
		    {			
				int id;
				std::string name,aux;
			    //---*Buscamos el Source de la zona:
				if(Line.find("source=") != std::string::npos)
		        {	
					aux=Line;
					name=aux.substr(aux.find("source"));
					//---*Recortamos todo lo que este dentro de los corchetes, que es el dato que necesitamos:
					name=name.substr(name.find("[")+1);					
					name=name.erase(name.find("]"));					
				}
				//---*Buscamos el ID de la zona:
				if(Line.find("IDZona=") != std::string::npos)
		        {	
					aux=Line;
					std::string ID=aux.substr(aux.find("IDZona"));
					//---*Recortamos todo lo que este dentro de los corchetes, que es el dato que necesitamos:
					ID=ID.substr(ID.find("[")+1);					
					ID=ID.erase(ID.find("]"));					
					id=atoi(ID.c_str());						
				}

				pair<map<int,std::string>::iterator , bool> p= mapZonas.insert(pair<int,std::string>(id,name));
				
			  
			}
			}
			OpenFile.close();			
		}
		}
	}
	
	return true;
}

void LevelScene::ProcessEvent(const sf::Event &e){
	
	// si presionamos escape despliega la escena de pausa
	if(e.Type == sf::Event::KeyPressed && e.Key.Code == sf::Key::Escape){		
		//changetoMenu=true;			
		pause=true;
	}
	else
	theEngine.Get_Player1()->Event(e,theEngine.GetRenderWindow());	
	
}

void LevelScene::Update(const float &dt){	

	//---*Actualizamos la Zona actual del Nivel:
	if(!changeZona){
	theEngine.GetRenderWindow().SetView(zonaActual->GetView());
	zonaActual->Update(dt);	
	}
	
	//---*Esquema de como se compone una Zone (Scene de un Nivel)
    //---*Por ejemplo puede ser:
	//---*No Necesariamente poseeria todas las conexiones, pero el Nivel
	//---*posee un vector de strings que indican a que Zone corresponde cada conexion.
	//                        ZTM
    //                         |
    //					   ZLS----ZRS
    //				  ZLM---|  Z   |---ZRM
    //					   ZLI----ZRI
    //                         |
    //						  ZBM	
    //                                  Donde:	
    //										  ---*Z=ZONE    // ---*T=TOP   // ---*M=MEDIUM
	//                                        ---*B=BOTTOM  // ---*R=RIGHT // ---*I=INFERIOR
	//                                        ---*S=SUPERIOR// ---*L=LEFT  // ---*

	//---*Solo queda buscar aberturas, si las hay verifica que el player se salga del Rect del View
	//---*de todas formas, que se salga del view no significa que hay una nueva Zona, sino que
	//---*Antes verifica si tal zona existe o no con el string auxiliar "a"....
	int zonaAcargar=-1;
	//----*Si existe abertura hacia la Derecha, buscamos el nombre del archivo TXT de la Zona vecina hacia la Derecha (1)
	if(theEngine.Get_Player1()->GetPosition().x > zonaActual->GetAnchoZona()+5.0f)
	{	
	zonaAcargar=zonaActual->BuscoZonaVecinaenMapa(1);
	if(zonaAcargar!=-1)
	initPosSamus=2; //---*Alucard va a nacer en la abertura Izquierda de la nueva Zona
	}
	//----*Si existe abertura hacia la izq, buscamos el nombre del archivo TXT de la Zona vecina hacia la Izquierda (2)
	else if(theEngine.Get_Player1()->GetPosition().x < -5.0f)
	{
	zonaAcargar=zonaActual->BuscoZonaVecinaenMapa(2);
	if(zonaAcargar!=-1)
	initPosSamus=1; //---*Alucard va a nacer en la abertura Derecha de la nueva Zona
	}		
	//----*Si existe abertura hacia Arriba, buscamos el nombre del archivo TXT de la Zona vecina hacia Arriba (3)
	else if(theEngine.Get_Player1()->GetPosition().y<-5.0f)
	{	
	zonaAcargar=zonaActual->BuscoZonaVecinaenMapa(3);
	if(zonaAcargar!=-1)
	initPosSamus=3; //---*Alucard va a nacer en la abertura de Abajo de la nueva Zona
	}	
	//----*Si existe abertura hacia Abajo, buscamos el nombre del archivo TXT de la Zona vecina hacia Abajo (4)
	else if(theEngine.Get_Player1()->GetPosition().y-60.0f > zonaActual->GetAltoZona())
	{	
	zonaAcargar=zonaActual->BuscoZonaVecinaenMapa(4);
	if(zonaAcargar!=-1)
	initPosSamus=4; //---*Alucard va a nacer en la abertura de Arriba de la nueva Zona
	}	

	//---*Si el Zone existe, lo cargamos
	//---*Ajustamos algunas configuraciones para
	//---*Que se vea un pantallazo negro y enviamos la posicion donde tiene que aparecer Alucard.
	if(zonaAcargar!=-1)
	{		
	//---*Siempre que hay cambio de zona, vemos la scena de intercambio de zonas:	
    changeZona=true;	
	std::string aux="NULL"; //---*String auxiliar que va a tener el nombre de la Zona a Cargar.
	aux=BuscoZonaenMapa(mapZonas,zonaAcargar);
	ChangeZona(new Zona(aux,initPosSamus));
	ChangeZonaScene *z;
	z=new ChangeZonaScene();
	z->SetDireccion(initPosSamus);
	GameEngine::GetEngine().PushScene(z);
	GameEngine::GetEngine().Get_Player1()->JoystickOff();
	GameEngine::GetEngine().Get_Player1()->Setvy(0.0f);
	}
	//---*Cambia al Menu
	if(changetoMenu){
	//theEngine.GetRenderWindow().Clear(sf::Color::Black);
	//theEngine.GetRenderWindow().Display();	
	//changetoMenu=false;theEngine.ChangeScene(new MenuScene());
	}

   
}

void LevelScene::Render(sf::RenderWindow &w){
	zonaActual->Render(w);
	RenderMisc();
}


//------------[Dibujos Miscelaneos que pertenecen al nivel]-----------------------//
void LevelScene::RenderMisc(){	
	energia.SetPosition(theEngine.GetRenderWindow().GetView().GetRect().Left + 5 ,theEngine.GetRenderWindow().GetView().GetRect().Top + 5);
	theEngine.GetRenderWindow().Draw(energia);
}

//---*Libera memoria de la Zona eliminada y cambia el puntero de la zona actual.
void LevelScene::LimpiayReordena(){
	// manejo de escenas: si hubo algun cambio de escena, lo hace efectivo
		if (m_mustUpdateCurrentZona){
			m_mustUpdateCurrentZona=false;
			// si hay alguna escena por borrar/desinicializar, lo hace
			if (zonaAeliminar != NULL){				
				delete zonaAeliminar;
				zonaAeliminar = NULL;
				changeZona=false;
			}
			// pone como escena actual la escena del frente de la cola
			if (m_zonasDeque.size()>0){
				zonaActual = m_zonasDeque.front();		
				//---*Vemos si es necesario cambiar la musica de fondo:
		        if(musicFile != zonaActual->music){	
				   //MusicManager::GetInstance().Play(musicFile);		  
		        }		
			}
			else
				zonaActual = NULL;
		}

		
}


//---*Cambia la Zona por la nueva Zona, elimina la anterior. el entero es para posicionar al player:
void LevelScene::ChangeZona(Zona *z){		
	PopZona();		// quita la Zona actual de la cola
	PushZona(z);	// empuja otra Zona en su posicion
	LimpiayReordena();
	
}

void LevelScene::PushZona(Zona *z){
	z->Init();	
	z->Aniade_Player1(theEngine.Get_Player1());	
	m_zonasDeque.push_front(z);
	m_mustUpdateCurrentZona = true;	
	//---*Vemos si es necesario cambiar la musica de fondo
	if(musicFile!=z->music)	{		
    m_mustChangeMusic=true;
	}

	
}

void LevelScene::PopZona(){
	zonaAeliminar = m_zonasDeque.front();
	m_zonasDeque.pop_front();
	m_mustUpdateCurrentZona = true;
	
}

void LevelScene::AppendZona(Zona *z){
	if ( m_zonasDeque.size() == 0)
		zonaActual = z;
	m_zonasDeque.push_back(z);
	z->Init();	
	z->Aniade_Player1(theEngine.Get_Player1());
	
}

void LevelScene::EndCurrentZona(){
	PopZona();
}


